package com.holly.unit.deform.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.holly.unit.core.enums.QueryRuleEnum;
import com.holly.unit.deform.context.DeformContext;
import com.holly.unit.deform.entity.DesignFormData;
import com.holly.unit.es.api.EsApi;
import com.holly.unit.es.api.config.EsConfig;
import com.holly.unit.es.api.enums.EsTypeEnum;
import com.holly.unit.es.api.pojo.EsProperties;
import com.holly.unit.es.api.pojo.QueryStringBuilder;
import com.holly.unit.query.api.enums.MatchTypeEnum;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 类描述: ElasticSearch查询包装类工具类
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/4/6 10:34
 */
@Slf4j
public class EsQueryWrapperUtil {

    private static EsApi esApi = DeformContext.esApi();


    public static Boolean fillQueryWrapper(String desformCode, QueryWrapper<DesignFormData> queryWrapper, JSONArray superQueryList, MatchTypeEnum matchType) {
        if (superQueryList != null && superQueryList.size() != 0) {
            String typeName = "design_form";
            JSONArray must = new JSONArray();
            JSONArray should = new JSONArray();
            Map<String, QueryStringBuilder> builderMap = new HashMap<>();
            // 获取 es 索引字段类型
            Map<String, EsProperties> mappings = esApi.getIndexMappingFormat(desformCode, typeName, EsProperties.class);
            log.info("ES索引字段类型: " + JSON.toJSONString(mappings));
            log.info("高级查询列表: " + JSON.toJSONString(superQueryList));
            for (int i = 0; i < superQueryList.size(); i++) {
                JSONObject queryItem = superQueryList.getJSONObject(i);
                String val = queryItem.getString("val");
                String type = queryItem.getString("type");
                String field = queryItem.getString("field");
                // 是否是keyword类型（字符串）
                boolean isKeyword = (!"number".equals(type) && !"date".equals(type) && !"time".equals(type));
                // 获取该字段的实际类型
                EsProperties esProperties = mappings.get(field);
                if (esProperties != null && esProperties.getType() != null) {
                    EsTypeEnum esType = esProperties.getType();
                    // 判断特殊类型，不加 keyword
                    if (esType == EsTypeEnum.DATE
                            // 整数类型
                            || esType == EsTypeEnum.INTEGER
                            || esType == EsTypeEnum.LONG
                            || esType == EsTypeEnum.SHORT
                            || esType == EsTypeEnum.BYTE
                            // 浮点类型
                            || esType == EsTypeEnum.DOUBLE
                            || esType == EsTypeEnum.FLOAT
                            || esType == EsTypeEnum.HALF_FLOAT
                            || esType == EsTypeEnum.SCALED_FLOAT
                            // 逻辑类型
                            || esType == EsTypeEnum.BOOLEAN
                            ) {
                        isKeyword = false;
                    }
                }
                // 字符串类型需要加上 keyword 才能查出来数据
                if (isKeyword) {
                    field += ".keyword";
                }
                QueryRuleEnum rule = QueryRuleEnum.getByValue(queryItem.getString("rule"));
                if (rule != null) {
                    switch (rule) {
                        case GT: // 大于
                            addQueryByMatchType(esApi.buildRangeQuery(field, val, null, false, false), matchType, must, should);
                            break;
                        case GE: // 大于等于
                            addQueryByMatchType(esApi.buildRangeQuery(field, val, null, true, false), matchType, must, should);
                            break;
                        case LT: // 小于
                            addQueryByMatchType(esApi.buildRangeQuery(field, null, val, false, false), matchType, must, should);
                            break;
                        case LE: // 小于等于
                            addQueryByMatchType(esApi.buildRangeQuery(field, null, val, false, true), matchType, must, should);
                            break;
                        case EQ: // 等于
                            addQueryStringByMatchType(builderMap, field, val, matchType, true);
                            break;
                        case NE: // 不等于
                            notQueryString(builderMap, field, val);
                            break;
                        case IN: // 在...中
                            String[] valArr = val.split(",");
                            if (valArr.length > 0) {
                                QueryStringBuilder inBuilder = new QueryStringBuilder(field, valArr[0], false);
                                for (int j = 1; j < valArr.length; j++) {
                                    inBuilder.or(valArr[j]);
                                }
                                addQueryByMatchType(esApi.buildQueryString(inBuilder.toString()), matchType, must, should);
                            }
                            break;
                        case LIKE: // 包含
                            addQueryStringByMatchType(builderMap, field, "*" + val + "*", matchType, false);
                            break;
                        case LEFT_LIKE: // 以..结尾
                            addQueryStringByMatchType(builderMap, field, "*" + val, matchType, false);
                            break;
                        case RIGHT_LIKE: // 以..开始
                            addQueryStringByMatchType(builderMap, field, val + "*", matchType, false);
                            break;
                    }
                }
            }
            for (String key : builderMap.keySet()) {
                String queryString = builderMap.get(key).toString();
                addQueryByMatchType(esApi.buildQueryString(queryString), matchType, must, should);
            }
            try {
                // 只返回 id 字段，多余的不返回，减轻带宽负担
                List<String> _source = Collections.singletonList("_id");
                JSONObject query = esApi.buildQuery(_source, esApi.buildBoolQuery(must, null, should), 0, EsConfig.esMaxSize);
                JSONObject searchResult = esApi.search(desformCode, typeName, query);
                JSONArray records = searchResult.getJSONObject("hits").getJSONArray("hits");
                if (records.size() > 0) {
                    List<String> ids = new ArrayList<>();
                    for (int i = 0; i < records.size(); i++) {
                        JSONObject record = records.getJSONObject(i);
                        ids.add(record.getString("_id"));
                    }
                    queryWrapper.in("id", ids);
                } else {
                    // 没有查询到任何结果，添加一个永远都不成立的条件，使其查询不出来数据
                    queryWrapper.eq("1", "2");
                }
                return true;
            } catch (Exception e) {
                log.error("ES索引数据查询失败：" + e.getMessage(), e);
                return false;
            }
        }
        return null;
    }







    // 根据 matchType 添加查询条件
    public static void addQueryByMatchType(Object query, MatchTypeEnum matchType, JSONArray must, JSONArray should) {
        if (matchType == MatchTypeEnum.OR) {
            should.add(query);
        } else {
            must.add(query);
        }
    }

    // 根据 matchType 添加查询条件
    public static void addQueryStringByMatchType(Map<String, QueryStringBuilder> map, String field, String value, MatchTypeEnum matchType, boolean addQuot) {
        QueryStringBuilder builder = getQueryString(map, field, value, false, addQuot);
        if (builder != null) {
            if (matchType == MatchTypeEnum.OR) {
                builder.or(value, addQuot);
            } else {
                builder.and(value, addQuot);
            }
        }
    }

    public static void andQueryString(Map<String, QueryStringBuilder> map, String field, String value) {
        QueryStringBuilder builder = getQueryString(map, field, value);
        if (builder != null) {
            builder.and(value);
        }
    }

    public static void notQueryString(Map<String, QueryStringBuilder> map, String field, String value) {
        QueryStringBuilder builder = getQueryString(map, field, value, true, true);
        if (builder != null) {
            builder.not(value);
        }
    }

    public static QueryStringBuilder getQueryString(Map<String, QueryStringBuilder> map, String field, String value) {
        return getQueryString(map, field, value, false, true);
    }

    public static QueryStringBuilder getQueryString(Map<String, QueryStringBuilder> map, String field, String value, boolean not, boolean addQuot) {
        QueryStringBuilder builder = map.get(field);
        if (builder == null) {
            builder = new QueryStringBuilder(field, value, not, addQuot);
            map.put(field, builder);
            return null;
        }
        return builder;
    }

}
